package braden.cis.uab.edu.automaton;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.Window;
import android.widget.EditText;
import android.widget.Toast;
import braden.cis.uab.edu.ModelConverter;
import braden.cis.uab.edu.R;
import braden.cis.uab.edu.grammar.CFGEditorActivity;

public class AutomatonDrawingActivity extends Activity {

	private AutomatonDrawingPanel panel;
	private Automaton automatonOne;
	private ClosureOperation currentOperation;

	private enum ClosureOperation {
		COMPLEMENT, CONCATENATE, INTERSECTION, MINUS, OVERLAP, KLEENE, UNION, REVERSE
	};

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// get rid of the title bar
		requestWindowFeature(Window.FEATURE_NO_TITLE);

		// create the drawing panel and set it as the content view
		panel = new AutomatonDrawingPanel(this);
		setContentView(panel);

		// //if we are displaying a regular expression
		// Bundle bundle = this.getIntent().getExtras();
		// if (bundle != null) {
		//			
		// //get it and display it
		// panel.drawAutomaton((Automaton) bundle.getSerializable("regex"));
		// }
	}

	// method called when the menu button is pressed
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {

		// get the options menu layout and inflate it
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.layout.panel_option_menu, menu);
		return true;
	}

	// method called when an item is selected in the options menu
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {

		// determine which button was pressed
		switch (item.getItemId()) {

		// if the user pressed the closure operation button
		case R.id.closure_operation_button:

			// if this is the second FA for a closure operation
			if (automatonOne != null) {

				// get the second FA and restore invariant
				Automaton automatonTwo = panel.getAutomaton();

				// set the options menu item back to its original title
				item.setTitle("Perform Closure Operation");

				// create a new panel to display our new FA
				panel = new AutomatonDrawingPanel(this);

				// perform the closure operation and draw the FA
				panel.drawAutomaton(performClosureOperation(automatonOne,
						automatonTwo));

				// set our variable back to null
				automatonOne = null;

				// set the content view to the panel so we can see our new FA
				setContentView(panel);

				// otherwise we need to let the user choose a closure operation
				// to perform
			} else {

				// ask the user what closure op to perform
				createClosureDialog(this);

				// set the itle of this item to done
				item.setTitle("Done");
			}
			break;

		// if the user wants to test a string with the current FA
		case R.id.test_string_button:

			// get the FA to test
			Automaton automatonToTest = panel.getAutomaton();

			// if we have a valid FA
			if (automatonToTest != null) {

				// test the string with the FA and display the result
				showTestOptions(this, automatonToTest);

				// if we dont have a valid FA
			} else {

				// tell the user
				Toast.makeText(this, "You must provide an initial state.",
						Toast.LENGTH_LONG).show();
			}
			break;

		// THIS WILL CONVERT AN NFA TO A DFA
		case R.id.convert_button:
			// get the FA to test
			Automaton automatonToConvert = panel.getAutomaton();

			// if we have a valid FA
			if (automatonToConvert != null) {

				// test the string with the FA and display the result
				showConvertOptions(this, automatonToConvert);

				// if we dont have a valid FA
			} else {

				// tell the user
				Toast.makeText(this, "You must provide an initial state.",
						Toast.LENGTH_LONG).show();
			}
			break;

		// if the user want to see information about their FA
		case R.id.info_button:

			// try to get the FA
			try {
				// get the FA and restore invariant
				Automaton automaton = panel.getAutomaton();

				// display the information
				showInfoDialog(automaton);

				// if something goes wrong, its because the user has not
				// provided an initial state and we dont have a valid FA
			} catch (NullPointerException e) {
				Toast.makeText(this, "You must provide an initial state.",
						Toast.LENGTH_LONG).show();
			}
			break;

		// if the user wants to clear the panel
		case R.id.clear_button:

			// replace the current panel with the new one and set the content
			// view
			panel = new AutomatonDrawingPanel(this);
			setContentView(panel);
		}
		return true;
	}

	private void showConvertOptions(
			final Context context,
			Automaton automatonToConvert) {
		AlertDialog alert;
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		String[] items = { "DFA", "Context Free Grammar" };
		builder.setTitle("Convert Options");
		builder.setItems(items, new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int item) {
				switch (item) {
				case 0:
					panel.drawAutomaton(panel.getAutomaton().convertToDFA());
					break;
				case 1:
					Intent intent = new Intent();
					Test.grammar = ModelConverter.automatonToGrammar(panel.getAutomaton());
					intent.setClass(context,
							CFGEditorActivity.class);
					startActivity(intent);
					break;
				}
			}
		});
		alert = builder.create();
		alert.show();
	}

	private void showTestOptions(final Context context,
			final Automaton automatonToTest) {
		AlertDialog alert;
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		String[] items = { "Quick Test", "Step Test" };
		builder.setTitle("Test Options");
		builder.setItems(items, new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int item) {
				switch (item) {
				case 0:
					createTestStringAlert(context, automatonToTest);
					break;
				case 1:
					AlertDialog.Builder stringTestAlert = new AlertDialog.Builder(
							context);
					stringTestAlert.setTitle("Step Test Automaton");
					stringTestAlert
							.setMessage("Enter a string to test with the automaton:");

					// Set an EditText view to get user input
					final EditText testStringInput = new EditText(context);
					stringTestAlert.setView(testStringInput);

					stringTestAlert.setPositiveButton("Ok",
							new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface dialog,
										int whichButton) {
									Intent intent = new Intent();
									intent.putExtra("stringtotest", testStringInput.getText().toString());
									//intent.putExtra("fatotest", Automaton.serializeObject(automatonToTest));
									Test.automaton = automatonToTest;
									intent.setClass(context,
											StepTestActivity.class);
									startActivity(intent);
								}

							});

					stringTestAlert.setNegativeButton("Cancel",
							new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface dialog,
										int which) {
								}
							});
					stringTestAlert.show();
					break;
				}
			}
		});
		alert = builder.create();
		alert.show();
	}

	// method for testing a string with an FA
	private void createTestStringAlert(final Context context,
			final Automaton automatonToTest) {

		// create an alert dialog
		AlertDialog.Builder testStringAlert = new AlertDialog.Builder(this);
		testStringAlert.setTitle("Test FA");
		testStringAlert.setMessage("Enter a string to test:");

		// Set an EditText view to get user input
		final EditText testInput = new EditText(this);
		testStringAlert.setView(testInput);

		testStringAlert.setPositiveButton("Ok",
				new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int whichButton) {
						String input = testInput.getText().toString();
						Toast.makeText(
								context,
								"The string was "
										+ (automatonToTest.testString(input
												.toCharArray()) ? "accepted."
												: "rejected."),
								Toast.LENGTH_LONG).show();
					}

				});

		testStringAlert.setNegativeButton("Cancel",
				new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int which) {

					}
				});
		testStringAlert.show();
	}

	private Automaton performClosureOperation(Automaton aOne, Automaton aTwo) {

		switch (currentOperation) {
		case COMPLEMENT:
			return aOne.getComplement();
		case CONCATENATE:
			return aOne.getConcatenation(aTwo);
			// case INTERSECTION:
			// return aOne.intersection(aTwo);
			// case MINUS:
			// aOne.minus(aTwo);
			// case OVERLAP:
			// aOne.overlap(aTwo);
		case KLEENE:
			return aOne.getKleeneStar();
		case REVERSE:
			return aOne.getReverse();
		case UNION:
			return aOne.getUnion(aTwo);
		default:
			return null;
		}
	}

	private void showInfoDialog(Automaton automaton) {
		AlertDialog.Builder infoAlert = new AlertDialog.Builder(this);
		infoAlert.setTitle("FA Information");
		infoAlert.setMessage("Number of state: "
				+ automaton.getNumberOfStates() + "\nNumber of transitions: "
				+ automaton.getNumberOfTransitions() + "\nDeterministic: "
				+ automaton.isDeterministic());
		infoAlert.show();
	}

	// method to ask the user what closure op they would like to perform
	private void createClosureDialog(final Context context) {

		// create an alert dialog builder
		AlertDialog alert;
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		String[] items = { "Complement", "Concatenate", "Kleene Star",
				"Reverse", "Union" };// , "Intersection", "Minus", "Overlap"
		builder.setTitle("Closure Options");
		builder.setItems(items, new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int item) {
				switch (item) {
				case 0:
					currentOperation = ClosureOperation.COMPLEMENT;
					break;
				case 1:
					currentOperation = ClosureOperation.CONCATENATE;
					break;
				// case 2:
				// currentOperation = ClosureOperation.INTERSECTION;
				case 2:
					currentOperation = ClosureOperation.KLEENE;
					break;
				// case 4:
				// currentOperation = ClosureOperation.MINUS;
				// case 5:
				// currentOperation = ClosureOperation.OVERLAP;
				case 3:
					currentOperation = ClosureOperation.REVERSE;
					break;
				case 4:
					currentOperation = ClosureOperation.UNION;
					break;
				}
				automatonOne = panel.getAutomaton();
				if (currentOperation != ClosureOperation.COMPLEMENT
						&& currentOperation != ClosureOperation.KLEENE
						&& currentOperation != ClosureOperation.REVERSE) {
					panel = new AutomatonDrawingPanel(context);
					setContentView(panel);
					Toast
							.makeText(
									context,
									"Build a second FA for the operation and select done in the options menu.",
									Toast.LENGTH_LONG).show();
				} else {
					panel = new AutomatonDrawingPanel(context);
					panel.drawAutomaton(performClosureOperation(automatonOne,
							null));
					setContentView(panel);
				}
			}
		});
		alert = builder.create();
		alert.show();
	}
}